package com.sunlands.deskmate.message.handler.sender.websocket;

import com.alibaba.fastjson.JSON;
import com.sunlands.deskmate.message.entity.Messages;
import com.sunlands.deskmate.message.enums.MessageType;
import com.sunlands.deskmate.channel.ChannelInfo;
import com.sunlands.deskmate.channel.ChannelManager;
import com.sunlands.deskmate.message.handler.receiver.MessageReceiverFactory;
import com.sunlands.deskmate.message.handler.sender.trigger.TriggerEvent;
import com.sunlands.deskmate.message.handler.sender.trigger.TriggerEventEnum;
import com.sunlands.deskmate.message.handler.sender.trigger.SendMessageTrigger;
import com.sunlands.deskmate.util.SpringUtils;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import lombok.extern.slf4j.Slf4j;
import java.util.Objects;

/**
 * @Author: anjunling@sunlands.com
 * @Description:
 * @Date: Create in 20:38 2018/6/5
 * @Modified By:
 */
@Slf4j
public abstract class AbstractWebsocketMessageSender<E> {

    boolean isDone;
    boolean isFinish;
    boolean isSuccess;
    protected ChannelInfo channelInfo;
    protected E message;
    private MessageReceiverFactory messageReceiverFactory;

    private MessageReceiverFactory getMessageReceiverFactory(){
        if(Objects.isNull(messageReceiverFactory)){
            synchronized (AbstractWebsocketMessageSender.class){
                if(Objects.isNull(messageReceiverFactory)){
                    messageReceiverFactory = SpringUtils.getBean(MessageReceiverFactory.class);
                }
            }
        }
        return messageReceiverFactory;
    }

    protected abstract ChannelFutureListener futureListener();

    protected abstract Integer strategy();

    public void send() {
        if (message == null) {
            log.warn("messages is empty");
            release();
            return;
        }

        try {
            //再次检查通道
            Channel channel = ChannelManager.getChannel(channelInfo.getSessionKey());
            if (channel == null) {
                release();
                log.warn("通道不存在 ，停止发送socket消息 channel second time not exists channelInfo {}, messages {}", channelInfo, JSON.toJSONString(message));
                return;
            }
            Integer strategy = strategy();
            do {
                channelSend(channel);
            } while (!isSuccess && !isFinish && strategy != null && strategy-- > 0);
            isDone = true;

            //通知拉取未读消息的事件，发送成功以后自动ACK（兼容安卓V3.1.0以前版本）
            Messages message = (Messages) this.message;
//            &&  MessageType.NOTICE_EVENT.getType().equals(message.getType())
            log.info("100、消息发送成功 sendResult {} isFinish {} isDone {} strategy {} channelInfo {} messages {}", isSuccess, isFinish, isDone, strategy, channelInfo, JSON.toJSONString(message));
            if(isDone){
                Messages ackMessage = Messages.builder()
                        .ackId(message.getAckId())
                        .type(MessageType.ACK.getType())
                        .fromUserId("系统自动ACK")
                        .build();
                getMessageReceiverFactory().getReceiver(ackMessage.getType()).receive(channel, ackMessage);
            }

        } catch (Exception e) {
            log.error(String.format("send fail channelInfo %s messages %s ", channelInfo, JSON.toJSONString(message)), e);
        }
    }

    private void channelSend(Channel channel) throws InterruptedException {
        //发送消息
        String messageText = JSON.toJSONString(this.message);

        ChannelFuture channelFuture = channel.writeAndFlush(new TextWebSocketFrame(messageText));

        log.debug("channelSend {} to channel {}", messageText, channel);

        channelFuture.addListener(futureListener());

        synchronized (this) {
            while (!isDone) {

                log.debug("send wait channelInfo {}", channelInfo);

                wait();

                log.debug("send wait get lock channelInfo {}", channelInfo);
            }
        }

        isDone = false;
    }

    void releaseChannel(Channel channel) {
        //回收资源
        log.debug("com.sunlands.sender.websocket.AbstractWebsocketMessageSender.releaseChannel");
        ChannelManager.release(channel);

        release();
    }


    void release() {
        //解锁
        SendMessageTrigger sendDispatcher = SpringUtils.getBean(SendMessageTrigger.class);
        TriggerEvent dispatchEvent = new TriggerEvent(TriggerEventEnum.UNSYNC, channelInfo);

        //TODO 这里的unlock和lock离的也太远了
        sendDispatcher.unlockEpoch(dispatchEvent);

        if (log.isDebugEnabled()) {
            log.debug("websocket release channelInfo {}", channelInfo);
        }
    }

}
